package com.topscomm.main.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.web.AuthenticationEntryPoint;

import com.topscomm.basic.exception.BusinessException;
import com.topscomm.security.endpoint.SecurityAuthenticationEntryPoint;
import com.topscomm.security.handler.SecurityAccessDeniedHandler;
import com.topscomm.security.oauth2.TapAccessTokenConverter;
import com.topscomm.security.oauth2.TapOAuth2AuthenticationEntryPoint;
import com.topscomm.security.oauth2.TapOauth2SecurityConfigurer;
import com.topscomm.security.oauth2.TapUserAuthenticationConverter;
import com.topscomm.security.properties.LoginProperties;
import com.topscomm.tap.security.TapSecurityConfigurer;

import lombok.AllArgsConstructor;

@Configuration
@AllArgsConstructor
@EnableResourceServer
@ConditionalOnExpression("'${login.token-create-type}'.equals('oauth2')")
public class Oauth2ServerConfig extends ResourceServerConfigurerAdapter {
	/**
	 * 权限不足异常处理器
	 */
	@Autowired
	private SecurityAuthenticationEntryPoint unauthorizedHandler;
	/**
	 * 禁止访问异常处理器
	 */
	@Autowired
	private SecurityAccessDeniedHandler accessDeniedHandler;
	@Autowired
	private final UserDetailsService userDetailsService;
	@Autowired
	private LoginProperties loginProperties;
	@Autowired(required = false)
	private List<TapSecurityConfigurer> tapSecurityConfigurers;

	/**
	 * @description:配置资源服务器的spring security
	 * 在ResourceServerConfiguration的configure方法中调用
	 * @param resources
	 * @author: zhangsui
	 * @date: 2020年4月14日下午11:39:25
	 * @modify:
	 */
	@Override
	public void configure(ResourceServerSecurityConfigurer resources) {
		resources.resourceId("*").stateless(true).tokenServices(remoteTokenServices());
		AuthenticationEntryPoint authenticationEntryPoint = new TapOAuth2AuthenticationEntryPoint();
		resources.authenticationEntryPoint(authenticationEntryPoint);
	}

	/**
	 * @description:配置资源服务器的spring security
	 * 在ResourceServerConfiguration的configure方法中调用
	 * @param httpSecurity
	 * @author: zhangsui
	 * @date: 2020年4月14日下午11:39:25
	 * @modify:
	 */
	@Override
	public void configure(HttpSecurity httpSecurity) throws Exception {
		httpSecurity
				// 禁用 CSRF
				.csrf().disable()
				// 授权异常
				.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
				.accessDeniedHandler(accessDeniedHandler)
				// 不创建会话
				.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
				.authorizeRequests().antMatchers("/auth/**").permitAll().antMatchers("/websocket/**").permitAll()
				.antMatchers("/druid/**").anonymous().antMatchers("/actuator/**").permitAll().antMatchers("/prop/**")
				.permitAll()
				// hessian服务
				.antMatchers(("/mpmHessian/**")).permitAll()
				// APP流程图查看
				.antMatchers("/cbo/approvalFlow/viewDiagramWithToken").permitAll()
				// APP下载附件
				.antMatchers("/cbo/attachment/download").permitAll()
				.antMatchers("/app/appLogin").permitAll()
				// 获取前端的配置参数
				.antMatchers("/prop/queryUIProperty").permitAll()
				// 获取服务端时间
				.antMatchers("/prop/queryServerTime").permitAll()
				// 密码验证规则
				.antMatchers("/cbo/user/queryPasswordVerifyRule").permitAll()
				// 发票系统回调
				.antMatchers("/pm/invoiceApply/invoiceCallback").permitAll()
				// APP待办数量接口
				.antMatchers("/app/appapproval/myMobileApprovalCount").permitAll()
				// swagger start
				.antMatchers("/swagger-ui.html").anonymous().antMatchers("/swagger-resources/**").anonymous()
				.antMatchers("/webjars/**").anonymous().antMatchers("/*/api-docs").anonymous()
				// swagger end
				.antMatchers("/test/**").anonymous().antMatchers(HttpMethod.OPTIONS, "/**").anonymous()
				.antMatchers("/oauth/**").authenticated()
				// fms财经系统开放接口
				.antMatchers("/mpm/projectpolymerization/listForFMS").permitAll();
		// 平台自定义扩展
		if (tapSecurityConfigurers != null) {
			tapSecurityConfigurers.forEach(item -> {
				try {
					item.configure(httpSecurity);
				} catch (Exception e) {
					throw new BusinessException(e);
				}
			});
		}
		httpSecurity.authorizeRequests().anyRequest().authenticated();
		// 配置自定义configurer
		TapOauth2SecurityConfigurer configurer = new TapOauth2SecurityConfigurer(accessDeniedHandler);
		httpSecurity.apply(configurer);
	}

	/**
	 * @description: 在OAuth2AuthenticationProcessingFilter的doFilter中，通过Authentication
	 * authResult =
	 * authenticationManager.authenticate(authentication);执行认证
	 * OAuth2AuthenticationManager继承自AuthenticationManager，在方法authenticate中，通过OAuth2Authentication
	 * auth =
	 * tokenServices.loadAuthentication(token);调用远程认证服务器执行认证。
	 * @return
	 * @author: zhangsui
	 * @date: 2020年4月15日上午12:04:17
	 * @modify:
	 */
	@Bean
	public RemoteTokenServices remoteTokenServices() {
		RemoteTokenServices tokenService = new RemoteTokenServices();
		tokenService.setCheckTokenEndpointUrl(loginProperties.getOauth2Server() + "/oauth/check_token");
		tokenService.setClientId(loginProperties.getOauth2ClientId());
		tokenService.setClientSecret(loginProperties.getOauth2ClientSecure());
		tokenService.setAccessTokenConverter(accessTokenConverter());
		return tokenService;
	}

	/**
	 * @description:配置accessTokenConverter转换器
	 * @return
	 * @author: zhangsui
	 * @date: 2020年4月19日上午10:35:16
	 * @modify:
	 */
	private AccessTokenConverter accessTokenConverter() {
		TapAccessTokenConverter accessTokenConverter = new TapAccessTokenConverter();
		TapUserAuthenticationConverter userTokenConverter = new TapUserAuthenticationConverter();
		userTokenConverter.setUserDetailsService(userDetailsService);
		accessTokenConverter.setUserTokenConverter(userTokenConverter);
		return accessTokenConverter;
	}
}
